package org.qloudgen.txn.dilithium.cluster.thread;

import java.util.ArrayList;

import org.qloudgen.txn.dilithium.cluster.beans.Cluster;
import org.qloudgen.txn.dilithium.cluster.beans.ParameterBean;
import org.qloudgen.txn.dilithium.cluster.common.CommonValues;
import org.qloudgen.txn.dilithium.cluster.common.ShellExec;
import org.qloudgen.txn.dilithium.cluster.database.ClusterImpl;

public class ClusterThread extends Thread{

	private Cluster cluster;
	private String flag;
	private StringBuffer sb ;
	private String logFile;
	private Thread pgxc_ctl_log_thread;
	private boolean thread_is_runnable = true;
	
	public ClusterThread(Cluster cluster,String flag,StringBuffer sb ){
		this.cluster = cluster;
		this.flag = flag;
		this.sb = sb;
	}
	
	public void run(){
		
		this.logFile = CommonValues.LOGDIR + "/" + CommonValues.getLogFileName(this.cluster.getClusterID());
		
		// log thread
		this.pgxc_ctl_log_thread = new Thread() {
			public void run() {
	        	 ShellExec.logToWeb(logFile , sb, thread_is_runnable);
	         }
	     };
		
	     if (this.flag.equals(CommonValues.CLUSTER_INIT)){
				
			//create log file
	        int ret = ShellExec.createLogFile(this.logFile);
			
	        if (ret != 0) return ;
		    	this.pgxc_ctl_log_thread.start();
		    
		} else {
			this.pgxc_ctl_log_thread.start();
		}
	     
		if (this.flag.equals(CommonValues.CLUSTER_INIT)) initCluster();
		
		if (this.flag.equals(CommonValues.CLUSTER_ADD_NODE)) {
			
			for (String nodeIp : this.cluster.getNodeIpsList())
				addNode(nodeIp);
			
		}
		
		if (this.flag.equals(CommonValues.CLUSTER_REMOVE_NODE)) {
			
			for (String nodeIp : this.cluster.getNodeIpsList())
				removeNode(nodeIp);
			
		}
		
		if (this.flag.equals(CommonValues.CLUSTER_ADD_CLIENT)) {
			
			for (String nodeIp : this.cluster.getNodeIpsList())
				addClient(nodeIp);
			
		}
		
		if (this.flag.equals(CommonValues.CLUSTER_REMOVE_CLIENT)) {
			
			for (String nodeIp : this.cluster.getNodeIpsList())
				removeClient(nodeIp);
			
		}
		
        this.thread_is_runnable = false;
	}
	
	protected int initCluster(){
		
		// create coordExtraConfig/datanodeExtraConfig file based on parameters in metaDB.
		int ret = createParameterFile();
		if (ret != 0) return ret;
		 
		// construct no pass from controller node to all other nodes.
		if (CommonValues.CONSTRUCT_NO_PASS)
			ret = constructNoPass();
		if (ret != 0) return ret;
		
		ArrayList<String> nodeList = (ArrayList<String>) this.cluster.getNodeIpsList();
		
		// init cluster
		ret = ShellExec.initCluster(this.cluster,nodeList.get(0),this.logFile);
		if (ret != 0) return ret;
		
		// add gtmmaster/gtmslave to metadb
		ClusterImpl.updateClusterInfo(this.cluster);
		
		// add node one to metadb
		this.cluster.setNewNodeIp(nodeList.get(0));
		ClusterImpl.addNode(this.cluster);
		
		// add other nodes except first node :0
		for (int i=1; i<nodeList.size(); i++){
			
			// add remaining nodes
			addNode(nodeList.get(i));
			
		}
		
		ret = ShellExec.resetDBSuperUserPassword(this.cluster);
		if (ret != 0) return ret;
		
		// update cluster status from create to on
		return ret;
		
	}
	
	protected int addNode(String nodeIp){
		
		// construct no pass to new node
		if (this.flag.equals(CommonValues.CLUSTER_ADD_NODE) && CommonValues.CONSTRUCT_NO_PASS)
			copyKey(nodeIp,this.cluster.getSysUser(),this.cluster.getSysUserPassword());
		
		// add node
		int ret = ShellExec.clusterOperator(this.cluster,CommonValues.CLUSTER_ADD_NODE,nodeIp,this.logFile);
		
		if (ret != 0 ) return ret;
		
		// update metadb
		this.cluster.setNewNodeIp(nodeIp);
		ClusterImpl.addNode(this.cluster);
		
		return ret;
	}
	
    protected int removeNode(String nodeIp){
		
		// remove node
		int ret = ShellExec.clusterOperator(this.cluster,CommonValues.CLUSTER_REMOVE_NODE,nodeIp,this.logFile);
		
		if (ret != 0 ) return ret;
		
		// update metadb
		this.cluster.setNewNodeIp(nodeIp);
		ClusterImpl.removeNode(this.cluster);
		
		return ret;
	}
    
	protected int addClient(String nodeIp){
		
		// add client
		int ret = ShellExec.clusterOperator(this.cluster,CommonValues.CLUSTER_ADD_CLIENT,nodeIp,this.logFile);
		
		if (ret != 0 ) return ret;
		
		// update metadb
		this.cluster.setNewNodeIp(nodeIp);
		ClusterImpl.addClient(this.cluster);
		
		return ret;
	}
	
    protected int removeClient(String nodeIp){
		
		// remove client
		int ret = ShellExec.clusterOperator(this.cluster,CommonValues.CLUSTER_REMOVE_CLIENT,nodeIp,this.logFile);
		
		if (ret != 0 ) return ret;
		
		// update metadb
		this.cluster.setNewNodeIp(nodeIp);
		ClusterImpl.removeClient(this.cluster);
		
		return ret;
	}
    
	protected int createParameterFile(){
		
		/* 
		 * create coordExtraConfig/datanodeExtraConfig file based on parameters in metaDB. 
		*/
		
		/* get parameters from metadb */
		ArrayList<ParameterBean> paramList = (ArrayList<ParameterBean>) ClusterImpl.getClusterConf(this.cluster.getClusterID());
		
		// create parameter files
		int ret = ShellExec.createParameterFiles(this.cluster,paramList,this.sb,this.logFile);
		return ret;
	}
	
	protected int constructNoPass(){
		
		// generate ssh key on controller node
		int ret = generateKey(this.cluster.getControllerIp(),this.cluster.getSysUser(),this.cluster.getSysUserPassword());
		
		// copy ssh key to gtm nodes
		ret = copyKey(this.cluster.getGtmMasterIp(),this.cluster.getSysUser(),this.cluster.getSysUserPassword());
		if (this.cluster.getGtmSlaveIp() != null) 
			ret = copyKey(this.cluster.getGtmSlaveIp(),this.cluster.getSysUser(),this.cluster.getSysUserPassword());
		
		// copy ssh key to datanodes/coords
		for (String nodeIp : this.cluster.getNodeIpsList()){
			ret = copyKey(nodeIp,this.cluster.getSysUser(),this.cluster.getSysUserPassword());
		}
		return ret;
	}
	
	private int generateKey(String controllerIp,String sysUser,String sysUserPassword){
		
		// generate ssh key on controller node
		int ret = ShellExec.generateSSHKey(controllerIp,sysUser,sysUserPassword);
		
		return ret;
	}
	
	private int copyKey(String nodeIp,String sysUser,String sysUserPassword){
		
		// copy ssh key to one node
		int ret = ShellExec.copySSHKey("nolocal",this.cluster.getControllerIp(),nodeIp,sysUser,sysUserPassword);
				
		return ret;
	}
}
